﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Net;
using R = System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Browser;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Xml.Linq;
using Microsoft.Practices.Composite.Presentation.Commands;
using Dimok.FilteredView;
using Dimok.Shared;

namespace Dimok {
	#region Extensions
	public static class ProfileModelExtensions {
		public static T GetParentDataContext<T>(this FrameworkElement d) where T : Model {
			while ((d != null) && !(d.DataContext is T))
				d = VisualTreeHelper.GetParent(d) as FrameworkElement;
			return d == null ? null : d.DataContext as T;
		}
	}
	#endregion
	public abstract class ProfileModelBase : Model {
		#region Delegates ====================
		protected delegate void GetQueryAsyncDelegate(string PageType, string ControlID, string ReportName, System.Collections.Generic.Dictionary<string, string> SortExpressions, System.Collections.Generic.Dictionary<string, string> Filters, object userState);
		protected delegate void GetDataSetAsyncDelegate(string EntityType, string[] Columns, string PageType, string ControlID, string ReportName, System.Collections.Generic.Dictionary<string, string> SortExpressions, System.Collections.Generic.Dictionary<string, string> Filters, object userState);
		protected delegate void SaveLayoutAsyncDelegate(string PageType, string ControlID, ReportProfile ReportProfile, object UserState);
		protected delegate void GetProfileAsyncDelegate(string PageType, string ControlID, string ReportName, object userState);
		#endregion ===========================

		#region Event/Delegates
		protected GetDataSetAsyncDelegate GetDataSetAsync;
		public class ProfileRemovedEventArgs : EventArgs {
			public string ProfileName { get; set; }
		}
		public class ProfileSavedEventArgs : EventArgs {
			public string ProfileName { get; set; }
		}
		public event EventHandler<ProfileRemovedEventArgs> ProfileRemoved;
		public event EventHandler<ProfileSavedEventArgs> ProfileSaved;
		protected virtual void OnProfileRemoved(string profileName) {
			if (ProfileRemoved == null) return;
			ProfileRemoved(this, new ProfileRemovedEventArgs() { ProfileName = profileName });
		}
		protected virtual void OnProfileSaved(string profileName) {
			if (ProfileSaved == null) return;
			ProfileSaved(this, new ProfileSavedEventArgs() { ProfileName = profileName });
		}
		public delegate IFilteredView GetFilteredSourceDelegate();
		public GetFilteredSourceDelegate GetFilteredSource;
		#endregion

		protected IFilteredView FilteredView { get; set; }

		private FilterDescriptionCollection _filterDescriptions = new FilterDescriptionCollection();
		public FilterDescriptionCollection FilterDescriptions1 {
			get { return _filterDescriptions; }
			set { _filterDescriptions = value; }
		}
		string _filtersDescription;

		public string FiltersDescription {
			get { return _filtersDescription; }
			set { 
				_filtersDescription = value;
				RaisePropertyChanged();
			}
		}

		#region IsLoading
		private bool isLoading = false;
		public bool IsLoading {
			get { return isLoading; }
			set {
				if (isLoading == value) return;
				isLoading = value;
				RaisePropertyChanged();
			}
		}
		public bool IsLoadingProfile { get; set; }
		#endregion
		public bool IsProfileLoaded { get; set; }

		#region Show Filter,Total,Average
		private bool? _showFilter;
		public bool? ShowFilter {
			get { return _showFilter; }
			set {
				if (_showFilter.HasValue && _showFilter == value) return;
				_showFilter = value;
				RaisePropertyChanged();
				if( !IsLoadingProfile )
					SavePageLayoutAsync(SelectedProfile.Value);
			}
		}
		private bool? _showTotals;
		public bool? ShowTotals {
			get { return _showTotals; }
			set {
				if (_showTotals.HasValue && _showTotals == value) return;
				_showTotals = value;
				RaisePropertyChanged();
				if (!IsLoadingProfile)
					SavePageLayoutAsync(SelectedProfile.Value);
			}
		}
		private bool? _showAverages;
		public bool? ShowAverages {
			get { return _showAverages; }
			set {
				if (_showAverages.HasValue && _showAverages == value) return;
				_showAverages = value;
				RaisePropertyChanged();
				if (!IsLoadingProfile)
					SavePageLayoutAsync(SelectedProfile.Value);
			}
		}
		#endregion


		#region Profiles =====================
		public class ProfileSelection : IEqualityComparer<ProfileSelection> {
			public ProfileModel Model { get; set; }
			public string Value { get; set; }
			public ProfileSelection() {

			}
			public ProfileSelection(ProfileModel model, string value) {
				Model = model;
				Value = value;
			}
			public override bool Equals(object obj) {
				var s = ((obj ?? "") is string ? obj + "" : ((ProfileSelection)obj).Value).ToLower();
				return this.Value.ToLower() == s;
			}
			public override int GetHashCode() {
				return Value.GetHashCode();
			}
			public override string ToString() {
				return this.Value;
			}

			#region IEqualityComparer<ProfileSelection> Members

			public bool Equals(ProfileSelection x, ProfileSelection y) {
				return x != null && y != null ? x.Value == y.Value : x == null && y == null;
			}

			public int GetHashCode(ProfileSelection obj) {
				return obj.GetHashCode();
			}

			#endregion
		}
		public ObservableCollection<ProfileSelection> ProfilesCollection = new ObservableCollection<ProfileSelection>() { new ProfileSelection(null, "") };
		#region Profile
		string _profileName = "";
		public string ProfileName {
			get { return _profileName; }
			set {
				_profileName = value;
			}
		}

		protected ProfileSelection _selectedProfile;
		public ProfileSelection SelectedProfile {
			get { return _selectedProfile; }
			set {
				if (_selectedProfile != value) {
					_selectedProfile = value;
					OnSelectedProfileChanged();
				}
			}
		}
		protected virtual void OnSelectedProfileChanged() {
			RaisePropertyChanged("SelectedProfile");
		}
		#endregion

		#endregion ===========================

		#region PageLayout
		public bool PageLayoutLoaded { get; set; }
		private string pageLayout = "";
		public string PageLayout {
			get { return pageLayout; }
			set {
				if (pageLayout == value) return;
				pageLayout = value;
				if (PageLayoutLoaded) {
					SavePageLayoutAsync(null,new Dimok.Shared.ReportProfile() { Docks = value },new CancelEventArgs());
				}
			}
		}
		public event EventHandler ResetPageLayout;
		public virtual void OnResetPageLayout() {
			if (ResetPageLayout == null) return;
			try {
				ResetPageLayout(this, new EventArgs());
			} catch (Exception exc) {
				throw exc;
			}
		}
		#endregion
		protected object ServiceObject { get; set; }
		protected string PageTypeName { get { return Application.Current.Host.Source.LocalPath.Split('/').Last(); } }
		public static string GetQueryTypeName(IFilteredView fv) { return fv.EntityType.Name; }
		#region SavePageLayoutAsync
		//private void SavePageLayoutAsync(ReportProfile reportPrifile) {
		//  IFilteredView FilteredView = GetFilteredSource();
		//  if (FilteredView == null) {
		//    MessageBox.Show("Unable to save layout without FilteredView property.");
		//    return;
		//  }
		//  reportPrifile.ReportName = FilteredView.ReportName();
		//  SavePageLayoutAsync(FilteredView, reportPrifile);
		//}
		public void SavePageLayoutAsync(string profileName) {
			var sender = GetFilteredSource != null ? GetFilteredSource() : null;
			SavePageLayoutAsync(sender,new ReportProfile(profileName), new CancelEventArgs());
		}
		public void SavePageLayoutAsync(object sender, CancelEventArgs e) {
			SavePageLayoutAsync(sender, new ReportProfile(), e);
		}
		public void SavePageLayoutAsync(object sender, ReportProfile reportProfile, CancelEventArgs e) {
			if (!IsProfileLoaded) return;
			IFilteredView filteredView = sender as IFilteredView;
			if (filteredView == null && GetFilteredSource != null)
				filteredView = GetFilteredSource();
			if (filteredView == null) {
				MessageBox.Show("Filtered Source is nop provided to " + GetType().FullName);
				return;
			}
			var cv = filteredView as ICollectionView;
			if (reportProfile.ReportName == null)
				reportProfile.ReportName = "";
			if (filteredView != null) {
				if (reportProfile.Columns == null && filteredView.Columns != null)
					reportProfile.Columns = filteredView.Columns();
				if (reportProfile.Sorts == null)
					reportProfile.Sorts = cv.SortDescriptions.ToDictionary(sd => sd.PropertyName, sd => (Dimok.Shared.ListSortDirection)sd.Direction);
				if (reportProfile.Filters == null)
					reportProfile.Filters = filteredView.FilterDescriptions.Filters().ToDictionary(fd => fd.Member, fd => fd.Value);
			}
			if (ShowFilter.HasValue) reportProfile.ShowFilter = ShowFilter;
			if (ShowTotals.HasValue) reportProfile.ShowTotals = ShowTotals;
			if (ShowAverages.HasValue) reportProfile.ShowAverages = ShowAverages;

			if (reportProfile.Docks == null && PageLayout != "")
				reportProfile.Docks = pageLayout;
			string methodName = "SaveLayoutAsync";
			var mi = ServiceObject.GetType().GetMethod(methodName,
				new Type[] { typeof(string), typeof(string), typeof(ReportProfile),typeof(object) });
			if (mi == null) throw new MissingMethodException("Method " + methodName + " was not found in " + ServiceObject.GetType().FullName);
			mi.Invoke(ServiceObject, new object[] { PageTypeName, GetQueryTypeName(filteredView), reportProfile, reportProfile.ReportName });
		}
		public void SaveLayoutCompleted(object sender, AsyncCompletedEventArgs e) {
			var msg = GetServiceError(e);
			if (msg != "") MessageBox.Show(msg);
			OnProfileSaved(e.UserState + "");
		}
		public void RemoveProfileCompleted(object sender, AsyncCompletedEventArgs e) {
			string msg = GetServiceError(e);
			if (msg != "") { MessageBox.Show(msg); return; }
			var profile = ProfilesCollection.FirstOrDefault(p => p.Value == e.UserState);
			if (profile != null) ProfilesCollection.Remove(profile);
			else MessageBox.Show("Unable repove profile.\nPropfile " + e.UserState + " does not exists.");

			OnProfileRemoved(e.UserState + "");
		}
		#endregion
		protected string GetServiceError(AsyncCompletedEventArgs e) {
			IsLoading = false;
			if (e.Error != null) return "Service Error:" + e.Error;
			var fc = e.GetPropertyOrNull("Fault") as Dimok.Shared.FaultContract;
			if (fc != null) return fc.Message;
			fc = e.GetPropertyOrNull("Result") as Dimok.Shared.FaultContract;
			if (fc != null) return fc.Message;
			return "";
		}
	}
	public class ProfileModel : ProfileModelBase {
		private DelegateCommand<TextBox> _HighlightControl;
		public DelegateCommand<TextBox> HighlightControl {
			get {
				return _HighlightControl;
			}
			protected set {
				_HighlightControl = value;
			}
		}
		DelegateCommand<object> showExcelCommand;
		public DelegateCommand<object> ShowExcelCommand {
			get { return showExcelCommand; }
			set { showExcelCommand = value; }
		}

		private List<object> initializedServices = new List<object>();
		public ProfileModel() {
			_selectedProfile = new ProfileSelection(this, "");
			Totals = new AggregateDictionary();
			Averages = new AggregateDictionary();
			ShowExcelCommand = new DelegateCommand<object>(o => { GetExcel(FilteredView); }, o => true);
			FilterDescriptions1.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(FilterDescriptions_CollectionChanged);
			FilterDescriptions1.FilterChanged += new PropertyChangedEventHandler(FilterDescriptions_FilterChanged);
		}

		void FilterDescriptions_FilterChanged(object sender, PropertyChangedEventArgs e) {
			
		}

		void FilterDescriptions_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) {
			
		}
		public void InitEvents(object serviceObject, params IFilteredView[] views) {
			if (initializedServices.Contains(serviceObject)) return;
			GetDataSetAsync = Delegate.CreateDelegate(typeof(GetDataSetAsyncDelegate), serviceObject, "GetDataSetAsync") as GetDataSetAsyncDelegate;
			HookEvent(serviceObject, "GetProfileCompleted");
			HookEvent(serviceObject, "RemoveProfileCompleted");
			HookEvent(serviceObject, "GetDataSetCompleted");
			HookEvent(serviceObject, "SaveLayoutCompleted");
			initializedServices.Add(serviceObject);
			foreach (var view in views) {
				view.ColumnsChanged += SavePageLayoutAsync;
				view.FiltersChanged += (s, e) => {
					FiltersDescription = view.FilterDescriptions.Filters().ToString();
					SavePageLayoutAsync(s, e);
				};
			}
			//getProfileCompletedEvent += new EventHandler<TGetProfileCompletedEventArgs>(GetProfileCompleted);
		}

		#region Helpers
		public static TService InitServiceClient<TService>(string basicHttpBinding, string serviceUrl) where TService : class {
			var uri = HtmlPage.Document.DocumentUri;
			string hostUrl = uri.Scheme + "://" + uri.Host + ":" + uri.Port + serviceUrl;
			try {
				var c = typeof(TService).GetConstructor(new Type[] { typeof(string), typeof(System.ServiceModel.EndpointAddress) });
				return c.Invoke(new object[] { basicHttpBinding, new System.ServiceModel.EndpointAddress(hostUrl) }) as TService;
				//return new MBCService.SLServiceClient(s, new System.ServiceModel.EndpointAddress(hostUrl));
			} catch (Exception exc) {
				HtmlPage.Window.Alert(exc.Message);
				throw exc;
			}
		}
		void HookEvent(object serviceObject, string eventName) {
			try {
				R.EventInfo ei = serviceObject.GetType().GetEvent(eventName);
				if (ei == null) throw new MissingMemberException("Event " + eventName + " was not found in " + serviceObject.GetType().FullName);
				var eventAdd = ei.GetAddMethod();
				var method = this.GetType().GetMethods().SingleOrDefault(m => m.Name == eventName);
				object[] d = { 
											 method!=null?

											 Delegate.CreateDelegate(ei.EventHandlerType, this, method) :
											 Delegate.CreateDelegate(ei.EventHandlerType, this, eventName) 
										 };
				eventAdd.Invoke(serviceObject, d);
			} catch (Exception exc) {
				throw new MissingMethodException(eventName, exc);
			}
		}
		#endregion

		#region Static Commons
		protected virtual void Init(object serviceObject, IFilteredView view) {
			FilteredView = view;
			ServiceObject = serviceObject;
			InitEvents(serviceObject, view);
			view.OnRefresh += (sender, e) => { GetDataSet(view); };
		}
		public void GetProfile(string profileName) { GetProfile(FilteredView, profileName); }
		public void GetProfile(IFilteredView view, string profileName) {
			var mi = ServiceObject.GetType().GetMethod("GetProfileAsync", new Type[] { typeof(string), typeof(string), typeof(string), typeof(object) });
			if (mi == null) throw new MissingMethodException("Method GetProfileAsync was not found in " + ServiceObject.GetType().FullName);
			mi.Invoke(ServiceObject, new object[] { PageTypeName, GetQueryTypeName(view), profileName, view });
			IsLoading = true;
			IsLoadingProfile = true;
		}
		public void RemoveProfile(string profileName) { RemoveProfile(FilteredView, profileName); }
		public void RemoveProfile(IFilteredView view, string profileName) {
			var mi = ServiceObject.GetType().GetMethod("RemoveProfileAsync", new Type[] { typeof(string), typeof(string), typeof(string), typeof(object) });
			if (mi == null) throw new MissingMethodException("Method RemoveProfileAsync was not found in " + ServiceObject.GetType().FullName);
			mi.Invoke(ServiceObject, new object[] { PageTypeName, GetQueryTypeName(view), profileName, profileName });
			IsLoading = true;
		}
		protected void SaveProfileAsync(object sender, ColumnsChangedEventArgs e/*, SaveProfileAsyncDelegate saveProfileAsync*/) {
			var fv = (IFilteredView)sender;
			var mi = ServiceObject.GetType().GetMethod("SaveProfileAsync",
				new Type[] { typeof(string), typeof(string), typeof(string), typeof(string[]), typeof(object) });
			if (mi == null) throw new MissingMethodException("Method SaveProfileAsync was not found in " + ServiceObject.GetType().FullName);
			mi.Invoke(ServiceObject, new object[] { PageTypeName, GetQueryTypeName(fv), e.ReportName, e.Columns, sender });
			((ICollectionView)sender).Refresh();
			IsLoading = true;
		}
		protected void GetExcel(IFilteredView view) {
			var fv = view as IFilteredView;
			var sc = view as ICollectionView;
			if (DataVisualization.DesignerProperties.GetIsInDesignMode() || fv == null) return;
			if (fv.Columns != null) {
				GetDataSetAsync(fv.EntityType.Name, fv.Columns(), PageTypeName, GetQueryTypeName(fv), ReportProfile.EXCEL_REPORT_NAME,
					sc.SortDescriptions.ToDictionary(sd => sd.PropertyName, sd => sd.Direction + ""),
					fv.FilterDescriptions.Filters().ToDictionary(fd => fd.Member, fd => fd.Value),
					view
					);
				IsLoading = true;
			} else System.Windows.Browser.HtmlPage.Window.Alert("Columns delegate is not defined in " + fv.EntityType.FullName + " collection.");
		}
		protected void GetDataSet(IFilteredView view){
			var fv = view as IFilteredView;
			var sc = view as ICollectionView;
			if (DataVisualization.DesignerProperties.GetIsInDesignMode() || fv == null) return;
			if (fv.Columns != null) {
				var profileName = "";
				GetDataSetAsync(fv.EntityType.Name, fv.Columns(), PageTypeName, GetQueryTypeName(fv), profileName,
					sc.SortDescriptions.ToDictionary(sd => sd.PropertyName, sd => sd.Direction + ""),
					fv.FilterDescriptions.Filters().ToDictionary(fd => fd.Member, fd => fd.Value),
					view
					);
				IsLoading = true;
			} else MessageBox.Show("Columns delegate is not defined in " + fv.EntityType.FullName + " collection.");
		}
		protected TEntity[] GetQuery<TEntity>(SortableCollectionView<TEntity> view, GetQueryAsyncDelegate getQueryDelegate) where TEntity : class, new() {
			var fv = view as IFilteredView;
			var sc = view as ICollectionView;
			if (DataVisualization.DesignerProperties.GetIsInDesignMode() || fv == null)
				return new TEntity[] { };
			var profileName = "";
			getQueryDelegate(PageTypeName, GetQueryTypeName(fv), profileName,
				sc.SortDescriptions.ToDictionary(sd => sd.PropertyName, sd => sd.Direction + ""),
				fv.FilterDescriptions.Filters().ToDictionary(fd => fd.Member, fd => fd.Value),
				view
				);
			IsLoading = true;
			return new TEntity[] { };
		}
		public class Aggregate<TValue> :Dimok.Model{
			TValue _value;
			public TValue Value {
				get { return _value; }
				set {
					if( _value.Equals(value) )return;
					_value = value;
					RaisePropertyChanged(() => Value);
					RaisePropertyChanged(() => FormatedValue);
				}
			}
			public string FormatedValue { get { return "n0"; } }
			public Aggregate() {}
			public Aggregate(TValue value) {
				this.Value = value;
			}
		}
		public class AggregateDictionary : Dictionary<string, Aggregate<double>> { }
		public AggregateDictionary Totals { get; set; }
		public AggregateDictionary Averages { get; set ; }
		public void GetProfileCompleted(object sender, AsyncCompletedEventArgs e) {
			try {
				string msg = GetServiceError(e);
				if (msg != "") { MessageBox.Show(msg); return; }
				var fv = e.UserState as IFilteredView;
				if (fv == null) throw new Exception("UserState must implement IFilteredView if used in GetProfileCompleted(...).");
				var profile = e.GetPropertyOrNull("Result") as Dimok.Shared.ReportProfile;
				if (profile == null) throw new MissingMemberException("Result property is missing in AsyncCompletedEventArgs object of GetProfileCompleted.");

				#region Sync ProfilesCollection =======================
				var delete = ProfilesCollection.Select(p => p.Value).Except(profile.ReportNames);
				ProfilesCollection.Where(p => delete.Contains(p.Value)).ToList().ForEach(p => ProfilesCollection.Remove(p));
				delete = profile.ReportNames.Except(ProfilesCollection.Select(p => p.Value));
				delete.ToList().ForEach(p => ProfilesCollection.Add(new ProfileSelection(this, p)));
				#endregion

				if (profile.ShowFilter.HasValue) ShowFilter = profile.ShowFilter;
				if (profile.ShowTotals.HasValue) ShowTotals = profile.ShowTotals;
				if (profile.ShowAverages.HasValue) ShowAverages = profile.ShowAverages;


				if (profile.MetaTables.ContainsKey(fv.EntityType.Name)) {
					var metaFields = profile.MetaTables[fv.EntityType.Name];
					Averages.Clear();
					metaFields.Where(mf => mf.Value.IsAverage).ToList().ForEach(mf => Averages.Add(mf.Key, new Aggregate<double>()));
					Totals.Clear();
					metaFields.Where(mf => mf.Value.IsTotal).ToList().ForEach(mf => Totals.Add(mf.Key, new Aggregate<double>()));
				}
				IsProfileLoaded = true;
				fv.OnGridProfileLoaded(new GridProfileLoadedEventArgs(profile));
			} finally {
				IsLoadingProfile = false;
			}
		}
		public void SaveProfileCompleted(object sender, AsyncCompletedEventArgs e) {
			string msg = GetServiceError(e);
			if (msg != "") MessageBox.Show(msg);
		}

		public void GetDataSetCompleted(object sender, AsyncCompletedEventArgs e) {
			string msg = GetServiceError(e);
			if (msg != "") {
				MessageBox.Show(msg);
				return;
			}
			var view = e.UserState as ICollectionView;
			if (view == null) throw new Exception("UserState must implement ICollectionView if GetQueryCompleted(...) is used.");
			var source = view.SourceCollection as IList;
			if (source == null) throw new Exception("SourceCollection in ICollectionView must implement IList if GetQueryCompleted(...) is used.");
			object result = null;
			if (!e.TryGetProperty("Result", out result)) throw new Exception("AsyncCompletedEventArgs parameter must have Result property when used in GetQueryCompleted(...)");
			if (!(result is string)) throw new Exception("Result property must be a string when used in GetDataSetCompleted(...)");
			Guid guidKey;
			if ((result + "").TryParse(out guidKey)) {
				var address = ReportProfile.EXCEL_REPORT_URL + "?" + Dimok.Shared.ReportProfile.EXCEL_REPORT_KEY + "=" + guidKey.ToString();
				var filenName = (HtmlPage.Window.Prompt("Excel file name:") + "").Replace("undefined", "");
				if (filenName != "") address += "&FileName=" + filenName;
				var uri = new Uri(address, UriKind.Relative);
				if( filenName == "") HtmlPage.Window.Navigate(uri, "_blank");
				else HtmlPage.Window.Navigate(uri);
			} else {
				source.Clear();
				var ds = new Silverlight.DataSet();
				ds.FromXml(result + "", true);
				var fv = view as IFilteredView;
				var entityProperties = fv.EntityType.GetProperties();
				var dt = ds.Tables[0];
				foreach (var row in Silverlight.DataSet.GetRows(XElement.Parse(result + ""), dt.TableName)) {
					var entity = Activator.CreateInstance(fv.EntityType);
					foreach (var column in row.Elements())
						entity.SetProperty(column.Name.LocalName, column.Value);
					source.Add(entity);
				}
				if (ds.Tables.Any(at => at.TableName == "AVG"))
					FillAggregates(Averages, result + "", "AVG");
				if (ds.Tables.Any(at => at.TableName == "SUM"))
					FillAggregates(Totals, result + "", "SUM");
				if (fv != null) fv.OnRefreshed();
			}
		}

		static void FillAggregates(AggregateDictionary ad,string xmlDataSet,string dataTableName) {
			foreach (var row in Silverlight.DataSet.GetRows(XElement.Parse(xmlDataSet + ""), dataTableName))
				foreach (var element in row.Elements())
					ad[element.Name.LocalName].Value = double.Parse(element.Value);
		}

		protected void GetQueryCompleted<TAsyncCompletedEventArgs>(object sender, TAsyncCompletedEventArgs e) where TAsyncCompletedEventArgs : AsyncCompletedEventArgs {
			string msg = GetServiceError(e);
			if (msg != "") {
				MessageBox.Show(msg);
				return;
			}
			var view = e.UserState as ICollectionView;
			if (view == null) throw new Exception("UserState must implement ICollectionView if GetQueryCompleted(...) is used.");
			var source = view.SourceCollection as IList;
			if (source == null) throw new Exception("SourceCollection in ICollectionView must implement IList if GetQueryCompleted(...) is used.");
			object result = null;
			if (!e.TryGetProperty("Result", out result)) throw new Exception("AsyncCompletedEventArgs parameter must have Result property when used in GetQueryCompleted(...)");
			if (!(result is IEnumerable)) throw new Exception("Result property must implement IEnumerable when used in GetQueryCompleted(...)");
			source.Clear();
			foreach (var data in result as IEnumerable)
				source.Add(data);
			var fv = view as IFilteredView;
			if (fv != null) fv.OnRefreshed();
		}
		#endregion
	}
}
